
//canok 20210123
//NdkMediacodec.cpp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include "media/NdkMediaCodec.h"
#include "media/NdkMediaFormat.h"
//#include "geth264Frame.cpp"
#include <android/native_window.h>
#include "1.1.cpp"
#define LOGD printf
bool bRun = true;
AMediaCodec* pMediaCodec;
AMediaFormat *format ;
//FILE *fp_out =NULL;
extern "C"{
	int Android_SurfaceWidth;
	int Android_SurfaceHeight;
	int  Android_JNI_SetOrientation();
	ANativeWindow*  Android_JNI_GetNativeWindow() ;
}
int64_t getTimeNsec() {
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC, &now);
	return (int64_t) now.tv_sec*1000*1000*1000 + now.tv_nsec;
}
int64_t getTimeSec() {
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC, &now);
	return (int64_t)now.tv_sec;
}
int64_t getTimeMsec(){ //毫秒
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC, &now);
	return now.tv_sec*1000 +(int64_t)now.tv_nsec/(1000*1000);
}
int64_t getTimeUsec(){ //us
	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC, &now);
	return now.tv_sec*1000*1000 +(int64_t)now.tv_nsec/(1000);
}
int firstFrames =0;
void *run(void*pram){/*
			if(fp_out ==NULL){
			fp_out = fopen("/storage/emulated/0/canok/yuv.data","w+");
			if(fp_out==NULL){
			LOGD("fopen erro!\n");
			return NULL;
			}
			}*/ 
	init(); 
	//init("/storage/emulated/0/canok/test.h264");
	//https://github.com/android/ndk-samples/blob/main/native-codec/app/src/main/cpp/native-codec-jni.cpp
	//decode
	//这里设定名称
	// pMediaCodec = AMediaCodec_createCodecByName("video/avc");//h264
	pMediaCodec = AMediaCodec_createDecoderByType("video/avc");//h264
	format = AMediaFormat_new();
	AMediaFormat_setString(format, "mime", "video/avc");
	AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_WIDTH,1088);
	AMediaFormat_setInt32(format,AMEDIAFORMAT_KEY_HEIGHT,1440);
	ANativeWindow* w=0; 
	LOGD("[%d%s%s]\n",__LINE__,__FUNCTION__,__DATE__);/*
	Android_JNI_SetOrientation();
	Android_SurfaceWidth=1080;
	Android_SurfaceHeight=1440; 
	w=Android_JNI_GetNativeWindow(); 
	*/
	//这里配置
	media_status_t status = AMediaCodec_configure(pMediaCodec,format,w,/*可以在这制定native surface, 直接渲染*/NULL,0);//解码，flags 给0，编码给AMEDIACODEC_CONFIGURE_FLAG_ENCODE
	if(status!=0){
		LOGD("erro config %d\n",status);
	}

	//启动
	AMediaCodec_start(pMediaCodec);
	int outFramecount = 0;
	int inFramecount =0; 
	while(bRun){
		//无法做到理想的入一帧，就解码输出该帧。 解码需要参考。现在是多次输入，每一次输入成功都把当前所有已经解码完的取出。

		//1.0 取空buffer，填充数据，入队
		ssize_t bufidx = AMediaCodec_dequeueInputBuffer(pMediaCodec,2000);
		//如果配置错误，比如format中的格式和宽高没有配置，这里get 会出错（格式都不知道，怎么知道分配多大空间？），返回错误码，错误码的定义在？？？？
		//LOGD("input bufidx %d \n",bufidx);
		if(bufidx>=0){ //当取不到空buffer的时候，有可能是解码慢跟不上输入速度，导致buffer不够用，所以还需要在后面继续取解码后的数据。
			size_t bufsize;
			uint8_t *buf= AMediaCodec_getInputBuffer(pMediaCodec,bufidx,&bufsize);
			//get h264 frame: 并填充到 buf,
			//LOGD("bufsize %d %p\n",bufsize,buf);
			int h264FrameLen = getOneNal(buf,bufsize);
			if(h264FrameLen<=0){
				//需要销毁。。。。。。
				LOGD("get over!!!!!\n");
				break;
			}
			// presentationTimeUs    就是 PTS 如果不要求渲染，这里可以随便。 也可以更具这一个值，来确定每一帧的身份，解码完后的数据里也有这个值。
			//注意当前例子中，上面 getOneNal 并不是获取到一帧完整数据，有可能是 sps pps, 这种情况就不会有对应的 一帧输出。
			uint64_t presentationTimeUs = getTimeUsec();
			// LOGD("in framecount %d :%lld\n",inFramecount++,presentationTimeUs);
			//入队列 给到解码器
			AMediaCodec_queueInputBuffer(pMediaCodec,bufidx,0,h264FrameLen,presentationTimeUs,0);
		}


		//2.0 取输出，拿走数据，归还buffer
		size_t bufsize;
		uint8_t *buf=NULL;
		//free(buf); 
		AMediaCodecBufferInfo info;
		do{
			bufidx = AMediaCodec_dequeueOutputBuffer(pMediaCodec, &info, 2000);
			//取数据，一直到取到解码后的数据
			//LOGD("out bufidx %d \n",bufidx);
			if (bufidx >= 0) {
				int framelen = 0;
				{
					int mWidth, mHeight;
					auto format = AMediaCodec_getOutputFormat(pMediaCodec);
					AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth);
					AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight);
					int32_t localColorFMT;
					AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT,
							&localColorFMT);
					//framelen = mWidth * mHeight * 1.5; //这里干脆自己算大小了，是不是也应该有一个 键值存储可以直接来获取？
					framelen = info.size;
					//LOGD("out: outFramecount %d %lld  ", outFramecount,info.presentationTimeUs);
					LOGD("out:[%d]X[%d]%d,%d ",mWidth, mHeight, localColorFMT,   framelen); //21 == nv21格式 具体的定义在哪里？？？
				}
				//在这里取走解码后的数据，
				//然后释放buffer给解码器。
				buf = AMediaCodec_getOutputBuffer(pMediaCodec, bufidx, &bufsize);
				//LOGD("buf %p\n",buf); 
				LOGD("%d帧  [%ld:%ld]out data:%d \n", outFramecount++, getTimeSec(), getTimeMsec(),   bufsize);

				//bufsize 并不是有效数据的大小。
				//fwrite(buf,1,bufsize,fp_out);
				//fwrite(buf, 1, framelen, fp_out);
				write(2,buf,framelen); 
				//free(buf); 
				AMediaCodec_releaseOutputBuffer(pMediaCodec, bufidx, false);
				//free(buf);
			} else  if (bufidx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
				// 解码输出的格式发生变化
				int mWidth, mHeight;
				auto format = AMediaCodec_getOutputFormat(pMediaCodec);
				AMediaFormat_getInt32(format, "width", &mWidth);
				AMediaFormat_getInt32(format, "height", &mHeight);
				int32_t localColorFMT;

				AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT,
						&localColorFMT);
			}else if(bufidx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
			}else {
			}
		}while(bufidx>0);// 一直取到没数据，把之前解码完的都取出来
	}
}
int main(int argc, const char*argv[]){
	//if(argc != 4){
	//   LOGD("usage:filename,width,height\n");
	//   return -1;
	// }
	int ret =0;
	pthread_t pid;
	if((ret=pthread_create(&pid,NULL,run,NULL)) !=0 ){
		LOGD("thread_create err\n");
		return -1;
	}
	while(1){
		usleep(1000*1000);
	}

}


